<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>Point lighted cube (with animation)</title>
    <script src="lib/webgl-utils.js"></script>
    <script src="lib/webgl-debug.js"></script>
    <script src="lib/cuon-utils.js"></script>
    <script src="lib/cuon-matrix.js"></script>
    <style>
        body {
            border: 0;
            background-color: white;
        }
    </style>
</head>

<body onload="main()">
<canvas id="webgl" style="width:900px;height: 900px;">
    Please use a browser that supports "canvas"
</canvas>
</body>
<script>
    // 多个立方体  旋转  性能问题   着色问题
    var VSHADER_SOURCE =`
attribute vec4 a_Position;
attribute vec4 a_VertexPosition;
attribute vec4 a_Color;
attribute vec4 a_Normal;
uniform mat4 u_MvpMatrix;
uniform mat4 u_ModelMatrix;
uniform mat4 u_NormalMatrix;
uniform vec3 u_LightColor;
uniform vec3 u_LightPosition;
uniform vec3 u_AmbientLight;
varying vec4 v_Color;
//着色器程序针对每一个顶点执行
void main() {
  //计算点的位置
  gl_Position = u_MvpMatrix * a_Position;

  //物体的运动会导致表面的法向量方向发生变化，求出每个法向量在运动后的新值
  vec3 normal = normalize(vec3(u_NormalMatrix * a_Normal));

  // 单个点的位置
  vec4 vertexPosition = u_ModelMatrix * a_Position;

  //光线方向
  vec3 lightDirection = normalize(u_LightPosition - vec3(vertexPosition));

  //法向量和光线方向的点积
  float nDotL = max(dot(lightDirection, normal), 0.0);

  //反射光
  vec3 diffuse = u_LightColor * a_Color.rgb * nDotL;

  // 环境光
  vec3 ambient = u_AmbientLight * a_Color.rgb;

  // 表面颜色
  v_Color = vec4(diffuse + ambient, a_Color.a);
}
`;

    var FSHADER_SOURCE =`
#ifdef GL_ES
precision mediump float;
#endif
varying vec4 v_Color;
void main() {
  gl_FragColor = v_Color;
}`;

    var VSHADER_SOURCE2=`
attribute vec4 a_VertexPosition;
uniform mat4 u_MvpMatrix2;
void main(){
gl_Position=u_MvpMatrix2 * a_VertexPosition;
}
`;
    var FSHADER_SOURCE2=`
void main(){
gl_FragColor=vec4(1.0,1.0,1.0,1.0);
}
`;

    var vertices,normals,indices,colors, cubeLength=0.3,pointArray=[],translateCube,canvas,n,positionAttrib,colorAttrib,normalAttrib,edgeAttribute,u_LightColor,u_LightPosition,u_AmbientLight;
    function main() {

        canvas = document.getElementById('webgl');
        // 获取浏览器中画布的显示尺寸
        var displayWidth  = canvas.clientWidth;
        var displayHeight = canvas.clientHeight;
        if (canvas.width  !== displayWidth || canvas.height !== displayHeight) {
            canvas.width  = displayWidth;
            canvas.height = displayHeight;
        }


        var gl=getWebGLContext(canvas);
        // var gl = canvas.getContext('webgl',
        //     { antialias: true });
        if(!gl){
            console.log('无法获取webGL绘图环境');
            return;
        }
        gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
        gl.clearColor(0.0, 0.0, 0.0, 1.0);
        gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);


        var surfaceProgram=createProgram(gl,VSHADER_SOURCE,FSHADER_SOURCE);
        var edgeProgram=createProgram(gl,VSHADER_SOURCE2,FSHADER_SOURCE2);
        if(!surfaceProgram||!edgeProgram){
            console.log('创建程序对象失败');
            return;
        }

        gl.enable(gl.DEPTH_TEST);
        var start=new Date().getTime();
        var currentAngle=0.0;
        var tick=function(){
            currentAngle=animate(currentAngle);
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
            for(var i=0;i<5000;i++){
                drawCube(gl,surfaceProgram,i,currentAngle);
            }
            for(var l=0;l<5000;l++){
                drawLine(gl,edgeProgram,l,currentAngle);
            }
            requestAnimationFrame(tick);
        }
        tick();
        // console.log(new Date().getTime()-start)

    }

    function drawCube(gl,surfaceProgram,i,angle){
        gl.useProgram(surfaceProgram);
        // if(n==undefined){
        n = initVertexBuffers(gl,surfaceProgram);
        if (n < 0) {
            console.log('Failed to set the vertex information');
            return;
        }
        // }
        if(i==0){
            u_LightColor = gl.getUniformLocation(surfaceProgram, 'u_LightColor');
            u_LightPosition = gl.getUniformLocation(surfaceProgram, 'u_LightPosition');
            u_AmbientLight = gl.getUniformLocation(surfaceProgram, 'u_AmbientLight');
        }
        gl.uniform3f(u_LightColor, 1.0, 1.0, 1.0);
        gl.uniform3f(u_LightPosition, 2.3, 4.0, 3.5);
        gl.uniform3f(u_AmbientLight, 0.2, 0.2, 0.2);


        // Get the storage locations of uniform variables and so on
        var u_ModelMatrix = gl.getUniformLocation(surfaceProgram, 'u_ModelMatrix');
        var u_MvpMatrix = gl.getUniformLocation(surfaceProgram, 'u_MvpMatrix');
        var u_NormalMatrix = gl.getUniformLocation(surfaceProgram, 'u_NormalMatrix');
        // var u_LightColor = gl.getUniformLocation(surfaceProgram, 'u_LightColor');
        // var u_LightPosition = gl.getUniformLocation(surfaceProgram, 'u_LightPosition');
        // var u_AmbientLight = gl.getUniformLocation(surfaceProgram, 'u_AmbientLight');
        // if (!u_MvpMatrix || !u_NormalMatrix || !u_LightColor || !u_LightPosition　|| !u_AmbientLight) {
        //     console.log('Failed to get the storage location');
        //     return;
        // }

        // gl.uniform3f(u_LightColor, 1.0, 1.0, 1.0);
        // gl.uniform3f(u_LightPosition, 2.3, 4.0, 3.5);
        // gl.uniform3f(u_AmbientLight, 0.2, 0.2, 0.2);
        var modelMatrix = new Matrix4();  // Model matrix
        var mvpMatrix = new Matrix4();    // Model view projection matrix
        var normalMatrix = new Matrix4(); // Transformation matrix for normals
        modelMatrix.setRotate(90, 0, 1, 0); // Rotate around the y-axis

        translateCube={
            x:i-3,
            y:0,
            z:0
        };
        modelMatrix.setTranslate(translateCube.x,translateCube.y,translateCube.z);
        modelMatrix.rotate(angle, 0.0, 1.0, 0.0);
        gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);

        mvpMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100);
        mvpMatrix.lookAt(0, 6, 20, 0, 0, 0, 0, 1, 0);
        mvpMatrix.multiply(modelMatrix);
        gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);

        normalMatrix.setInverseOf(modelMatrix);
        normalMatrix.transpose();
        gl.uniformMatrix4fv(u_NormalMatrix, false, normalMatrix.elements);

        gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);

    }

    function drawLine(gl,edgeProgram,i,angle){
        gl.useProgram(edgeProgram);
        //画edge先的部分
        if(pointArray.length==0){
            getPointsArray();
            edgeAttribute=initArrayBuffer(gl,'a_VertexPosition', new Float32Array(pointArray), 3, gl.FLOAT,edgeProgram)
            // if(!initArrayBuffer(gl,'a_VertexPosition', new Float32Array(pointArray), 3, gl.FLOAT,edgeProgram)) return -1;
        }
        var u_MvpMatrix2 = gl.getUniformLocation(edgeProgram, 'u_MvpMatrix2');
        if(!u_MvpMatrix2){
            console.log('Failed to get the storage location');
            return;
        }
        var modelMatrix2 = new Matrix4();  // Model matrix
        var mvpMatrix2 = new Matrix4();    // Model view projection matrix
        translateCube={
            x:i-3,
            y:0,
            z:0
        };
        modelMatrix2.setTranslate(translateCube.x,translateCube.y,translateCube.z);
        modelMatrix2.rotate(angle, 0.0, 1.0, 0.0);

        mvpMatrix2.setPerspective(30, canvas.width/canvas.height, 1, 100);
        mvpMatrix2.lookAt(0, 6, 20, 0, 0, 0, 0, 1, 0);
        mvpMatrix2.multiply(modelMatrix2);
        gl.uniformMatrix4fv(u_MvpMatrix2, false, mvpMatrix2.elements);
        gl.drawArrays(gl.LINES,0,pointArray.length/3);

    }
    function getPointsArray(){
        var edge = [ 0, 0 ], edges = {}, edge1, edge2;
        var key, keys = [ 'a', 'b', 'c' ];
        for(var i=0,l=indices.length;i<l;i=i+3){
            var face={a:indices[i],b:indices[i+1],c:indices[i+2]};

            var normal=[normals[i],normals[i+1],normals[i+2]];

            for(var j=0;j<3;j++){
                edge1=face[keys[j]];
                edge2=face[keys[(j+1)%3]];
                edge[ 0 ] = Math.min( edge1, edge2 );
                edge[ 1 ] = Math.max( edge1, edge2 );
                key = edge[ 0 ] + ',' + edge[ 1 ];
                if ( edges[ key ] === undefined ) {
                    edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ], face1:face , face2: undefined };
                } else {
                    edges[ key ].face2 = face;
                }
            }
            // console.log(edges)
        }

        pointArray=[];
        for(key in edges){
            var e=edges[key];
            if(e.face2 === undefined) {
                pointArray.push(vertices[e.index1*3],vertices[e.index1*3+1],vertices[e.index1*3+2]);
                pointArray.push(vertices[e.index2*3],vertices[e.index2*3+1],vertices[e.index2*3+2]);
            }else{
                //e.face1.a ==>indices数组中顶点的索引
                //p1 ==> 顶点
                var p1={x:vertices[(e.face1.a)*3],y:vertices[(e.face1.a)*3+1],z:vertices[(e.face1.a)*3+2]};
                var p2={x:vertices[(e.face1.b)*3],y:vertices[(e.face1.b)*3+1],z:vertices[(e.face1.b)*3+2]};
                var p3={x:vertices[(e.face1.c)*3],y:vertices[(e.face1.c)*3+1],z:vertices[(e.face1.c)*3+2]};

                var q1={x:vertices[(e.face2.a)*3],y:vertices[(e.face2.a)*3+1],z:vertices[(e.face2.a)*3+2]};
                var q2={x:vertices[(e.face2.b)*3],y:vertices[(e.face2.b)*3+1],z:vertices[(e.face2.b)*3+2]};
                var q3={x:vertices[(e.face2.c)*3],y:vertices[(e.face2.c)*3+1],z:vertices[(e.face2.c)*3+2]};
                var face1Normal=getNormal(p1,p2,p3).elements;
                var face2Normal=getNormal(q1,q2,q3).elements;
                var dot=face1Normal[0]*face2Normal[0]+face1Normal[1]*face2Normal[1]+face1Normal[2]*face2Normal[2];
                if(dot<1){
                    pointArray.push(vertices[e.index1*3],vertices[e.index1*3+1],vertices[e.index1*3+2]);
                    pointArray.push(vertices[e.index2*3],vertices[e.index2*3+1],vertices[e.index2*3+2]);
                }

            }
        }
    }

    function getNormal(p1,p2,p3){
        var a = ( (p2.y - p1.y) * (p3.z - p1.z) - (p2.z - p1.z) * (p3.y - p1.y) );

        var b = ( (p2.z - p1.z) * (p3.x - p1.x) - (p2.x - p1.x) * (p3.z - p1.z) );

        var c = ( (p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x) );

        var normal=new Vector3([a,b,c])
        return normal.normalize();
    }

    function initVertexBuffers(gl,program) {
        // Create a cube
        //    v6----- v5
        //   /|      /|
        //  v1------v0|
        //  | |     | |
        //  | |v7---|-|v4
        //  |/      |/
        //  v2------v3
        // Coordinates

        if(vertices==undefined){
            vertices = new Float32Array([
                cubeLength, cubeLength, cubeLength,  -cubeLength, cubeLength, cubeLength,  -cubeLength,-cubeLength, cubeLength,   cubeLength,-cubeLength, cubeLength, // v0-v1-v2-v3 front
                cubeLength, cubeLength, cubeLength,   cubeLength,-cubeLength, cubeLength,   cubeLength,-cubeLength,-cubeLength,   cubeLength, cubeLength,-cubeLength, // v0-v3-v4-v5 right
                cubeLength, cubeLength, cubeLength,   cubeLength, cubeLength,-cubeLength,  -cubeLength, cubeLength,-cubeLength,  -cubeLength, cubeLength, cubeLength, // v0-v5-v6-v1 up
                -cubeLength, cubeLength, cubeLength,  -cubeLength, cubeLength,-cubeLength,  -cubeLength,-cubeLength,-cubeLength,  -cubeLength,-cubeLength, cubeLength, // v1-v6-v7-v2 left
                -cubeLength,-cubeLength,-cubeLength,   cubeLength,-cubeLength,-cubeLength,   cubeLength,-cubeLength, cubeLength,  -cubeLength,-cubeLength, cubeLength, // v7-v4-v3-v2 down
                cubeLength,-cubeLength,-cubeLength,  -cubeLength,-cubeLength,-cubeLength,  -cubeLength, cubeLength,-cubeLength,   cubeLength, cubeLength,-cubeLength  // v4-v7-v6-v5 back
            ]);

            // Colors
            colors = new Float32Array([
                1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v0-v1-v2-v3 front
                1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v0-v3-v4-v5 right
                1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v0-v5-v6-v1 up
                1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v1-v6-v7-v2 left
                1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v7-v4-v3-v2 down
                1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0　    // v4-v7-v6-v5 back
            ]);

            // Normal
            normals = new Float32Array([
                0.0, 0.0, 1.0,   0.0, 0.0, 1.0,   0.0, 0.0, 1.0,   0.0, 0.0, 1.0,  // v0-v1-v2-v3 front
                1.0, 0.0, 0.0,   1.0, 0.0, 0.0,   1.0, 0.0, 0.0,   1.0, 0.0, 0.0,  // v0-v3-v4-v5 right
                0.0, 1.0, 0.0,   0.0, 1.0, 0.0,   0.0, 1.0, 0.0,   0.0, 1.0, 0.0,  // v0-v5-v6-v1 up
                -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  // v1-v6-v7-v2 left
                0.0,-1.0, 0.0,   0.0,-1.0, 0.0,   0.0,-1.0, 0.0,   0.0,-1.0, 0.0,  // v7-v4-v3-v2 down
                0.0, 0.0,-1.0,   0.0, 0.0,-1.0,   0.0, 0.0,-1.0,   0.0, 0.0,-1.0   // v4-v7-v6-v5 back
            ]);

            // Indices of the vertices
            indices = new Uint8Array([
                0, 1, 2,   0, 2, 3,    // front
                4, 5, 6,   4, 6, 7,    // right
                8, 9,10,   8,10,11,    // up
                12,13,14,  12,14,15,    // left
                16,17,18,  16,18,19,    // down
                20,21,22,  20,22,23     // back
            ]);
            positionAttrib=initArrayBuffer(gl, 'a_Position', vertices, 3, gl.FLOAT,program);
            colorAttrib=initArrayBuffer(gl, 'a_Color', colors, 3, gl.FLOAT,program)
            normalAttrib=initArrayBuffer(gl, 'a_Normal', normals, 3, gl.FLOAT,program)

            // Unbind the buffer object
            // gl.bindBuffer(gl.ARRAY_BUFFER, null);
            // Write the indices to the buffer object
            var indexBuffer = gl.createBuffer();
            if (!indexBuffer) {
                console.log('Failed to create the buffer object');
                return false;
            }
            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
            gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
            return indices.length;
        }else{
            // gl.bindBuffer(gl.ARRAY_BUFFER, vertices);
            gl.enableVertexAttribArray(positionAttrib);
            gl.enableVertexAttribArray(colorAttrib);
            gl.enableVertexAttribArray(normalAttrib);
            // initAttributeVariable(gl,a_Position,)
            return indices.length;

        }
    }

    function initArrayBuffer(gl, attribute, data, num, type,program) {
        // Create a buffer object
        var buffer = gl.createBuffer();
        if (!buffer) {
            console.log('Failed to create the buffer object');
            return false;
        }
        // Write date into the buffer object
        gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
        gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
        // Assign the buffer object to the attribute variable
        var a_attribute = gl.getAttribLocation(program, attribute);
        if (a_attribute < 0) {
            console.log('Failed to get the storage location of ' + attribute);
            return false;
        }
        gl.vertexAttribPointer(a_attribute, num, type, false, 0, 0);
        // Enable the assignment of the buffer object to the attribute variable
        gl.enableVertexAttribArray(a_attribute);

        return a_attribute;
    }

    var ANGLE_STEP = 30;   // The increments of rotation angle (degrees)
    var last = Date.now(); // Last time that this function was called
    function animate(angle) {
        var now = Date.now();   // Calculate the elapsed time
        var elapsed = now - last;
        last = now;
        var newAngle = angle + (ANGLE_STEP * elapsed) / 1000.0;
        return newAngle % 360;
    }




</script>
</html>
